/*
 * Copyright TLR Team, Inc. All Rights Reserved.
 */
package com.tlr.device.rest;

import com.tfb.service.utility.JsonUtil;
import com.tfb.service.utility.Utils;
import com.tlr.device.model.DeviceMac;
import com.tlr.device.model.HikiData;
import com.tlr.device.model.HikiDevice;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.apache.log4j.Logger;

@ServerEndpoint("/hikesocket")
public class HikeWebsocketService {

    private static final Logger log = Logger.getLogger(HikeWebsocketService.class);
    private static final Map<String, List<Session>> macClients = Collections.synchronizedMap(new HashMap<String, List<Session>>());
    private static final Map<String, DeviceMac> idMacs = Collections.synchronizedMap(new HashMap<String, DeviceMac>());

    @SuppressWarnings("LeakingThisInConstructor")
    public HikeWebsocketService() {
        log.info("hiki web socket is loading");
        AppContext.setHikeSocketService(this);
    }

    @OnOpen
    public void onOpen(Session session) {
        // Add session to the connected sessions set
        log.info("Added a hike client: " + session.getId());
    }

    @OnMessage
    public String onMessage(String message, Session session) {
        try {
            DeviceMac deviceMac = JsonUtil.readValue(message, DeviceMac.class);
            if (deviceMac != null) {
                for (String mac : deviceMac.getMac_address()) {
                    log.info("Mac devices: " + macClients.size());
                    List<Session> sessionSubList = macClients.get(mac);
                    if (sessionSubList == null) {
                        sessionSubList = new ArrayList<>();
                        macClients.put(mac, sessionSubList);
                    }
                    sessionSubList.add(session);
                    log.info("Hike device mac: " + mac + ", session count:" + sessionSubList.size());
                }
                idMacs.put(session.getId(), deviceMac);
                return JsonUtil.writeValue(deviceMac);
            }
        } catch (IOException ex) {
            log.error(ex.fillInStackTrace().getMessage());
            return ex.fillInStackTrace().getMessage();
        }
        return "OK";
    }

    @OnClose
    public void onClose(Session session) {
        // Remove session from the connected sessions set
        DeviceMac macs = idMacs.remove(session.getId());
        for (String mac : macs.getMac_address()) {
            log.info("device " + mac + " is disconnected");
            List<Session> sessionSubList = macClients.get(mac);
            if (sessionSubList != null) {
                Iterator<Session> it = sessionSubList.iterator();
                while (it.hasNext()) {
                    if (it.next().getId().equals(session.getId())) {
                        it.remove();
                    }
                }
            }
        }
        log.info("Removed a hike client: " + session.getId());
    }

    public void notifyMessage(HikiDevice device) {
        String mac = device.getMac();
        try {
            List<Session> sessionSubList = macClients.get(mac);
            if (sessionSubList != null) {
                for (Session session : sessionSubList) {
                    log.info("device mac: " + mac + ", session: " + session.getId() + " is notified");
                    session.getAsyncRemote().sendText(JsonUtil.writeValue(device));
                }
            }
        } catch (Exception e) {
            log.error("Mac: " + mac + " device is failed to be notified for hike state");
            log.error(Utils.getFullException(e));
        }
    }

    public void notifyMessage(HikiData data) {
        String mac = data.getMac();
        try {
            List<Session> sessionSubList = macClients.get(mac);
            if (sessionSubList != null) {
                for (Session session : sessionSubList) {
                    session.getAsyncRemote().sendText(JsonUtil.writeValue(data));
                }
            }
        } catch (Exception e) {
            log.error("Mac: " + mac + " device is failed to be notified for hike data");
            log.error(Utils.getFullException(e));
        }
    }
}
